//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Microsoft.TridentWordAddIn.Common;
using Microsoft.TridentWordAddIn.ResourceHelper;
using Microsoft.TridentWordAddIn.Server;

namespace Microsoft.TridentWordAddIn.Presentation
{
    /// <summary>
    /// The Delegate for showing messages
    /// </summary>
    public delegate void ShowMessageDelegate(Exception ex, IWin32Window owner);

    /// <summary>
    /// User Control for Associating Workflow
    /// </summary>
    public partial class AssociateWorkflowTaskPane : BaseUserControl, ITaskPaneControl, IDisposable
    {
        private bool isAssociationCanceled = false, isError = false;
        private WorkflowJob selectedJob;
        private Workflow selectedWorkflow;
        private WorkflowAssociation workflowAssociation;
        private Thread asyncAssociation;
        private PictureRange range;

        /// <summary>
        /// Initializes a new instance of the <see cref="AssociateWorkflowTaskPane"/> class.
        /// </summary>
        public AssociateWorkflowTaskPane()
        {
            base.Initialize(false);
            this.InitializeComponent();
            this.InitializeUserControls();
            this.ControllerInstance.OnAssociationChanged += new EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
            this.ControllerInstance.OnDocumentClosing += new EventHandler<BaseEventArgs>(Controller_OnDocumentClosing);
        }
    
        /// <summary>
        /// Gets or sets the association mode.
        /// </summary>
        /// <value>The association mode.</value>
        public TaskPaneKey AssociationMode { get; set; }

        private delegate void UICallerDelegate(string message);

        #region Private Methods

        /// <summary>
        /// Initializes the user controls.
        /// </summary>
        private void InitializeUserControls()
        {
            this.ctrSelectWorkflow.OnSelectedJobChanged += new System.EventHandler<SelectedJobChangedEventArgs>(ctrSelectWorkflow_OnSelectedJobChanged);
            this.ctrSelectWorkflow.OnJobsUpdated += new EventHandler<JobsUpdatedEventArgs>(ctrSelectWorkflow_OnJobListUpdate);
            this.ctrSelectWorkflow.OnJobDoubleClicked += new EventHandler<EventArgs>(ctrSelectWorkflow_OnJobDoubleClicked);
            this.ctrSelectWorkflow.Initialize(true);
        }

        /// <summary>
        /// Statuses the message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void StatusMessage(string message)
        {
            this.toolStriplblStatus.Text = message;
        }

        /// <summary>
        /// Starts the workflow association.
        /// </summary>
        private void StartWorkflowAssociation()
        {
            this.isError = false;
            ITridentServer server = new TridentServer();
            try
            {
                server.CurrentTridentConnection = ControllerInstance.CurrentConnection;
                ControllerInstance.AssociateWorkflow(
                    DocumentKey,
                    this.selectedWorkflow.Clone() as Workflow,
                    this.workflowAssociation,
                    server);
            }
            catch (ThreadAbortException)
            {
                this.isError = true;
            }
            catch (ConnectionFailedException connException)
            {
                this.isError = true;
                MessageBoxHelper.ShowDefault(connException.Message, MessageBoxIcon.Error);
            }
            catch (Exception ex)
            {
                this.isError = true;
                this.BeginInvoke(new ShowMessageDelegate(MessageBoxHelper.ShowException), new object[] { ex, this});
            }
            finally
            {
                server.Dispose();
            }
        }

        /// <summary>
        /// Befores the association begin.
        /// </summary>
        private void BeforeAssociationBegin()
        {
            this.ControllerInstance.AssociationProgress += new EventHandler<ProgressEventArgs>(Controller_AssociationProgress);
            this.ControllerInstance.BeginModelTransaction(DocumentKey);
            this.btnSave.Enabled = false;
            this.btnCancel.Enabled = true;
            this.splitContainerTop.Enabled = false;
            this.pnlStoreWorkflow.Enabled = false;
            this.isAssociationCanceled = false;
        }

        /// <summary>
        /// Afters the association end.
        /// </summary>
        private void AfterAssociationEnd()
        {
            this.ControllerInstance.AssociationProgress -= new EventHandler<ProgressEventArgs>(Controller_AssociationProgress);
            this.btnSave.Enabled = true;
            this.btnCancel.Enabled = false;
            this.splitContainerTop.Enabled = true;
            this.pnlStoreWorkflow.Enabled = true;

            if (this.isAssociationCanceled)
            {
                this.StatusMessage("Workflow association cancelled by user");
                this.ControllerInstance.RollbackModelTransaction(DocumentKey);
            }
            else if (this.isError)
            {
                this.StatusMessage("Workflow association failed");
                this.ControllerInstance.RollbackModelTransaction(DocumentKey);
            }
            else
            {
                if (this.AssociationMode == TaskPaneKey.InsertTridentLink)
                {
                    this.ControllerInstance.InsertTridentLink(DocumentKey, this.range, this.workflowAssociation);
                }

                this.ControllerInstance.CommitModelTransaction(DocumentKey);
                this.ControllerInstance.AssociationChanged(DocumentKey, this.workflowAssociation.Picture);
            }
        }

        /// <summary>
        /// Creates the workflow association.
        /// </summary>
        /// <returns>WorkflowAssociation</returns>
        private WorkflowAssociation CreateWorkflowAssociation()
        {
            this.selectedWorkflow.Linked = this.rbnReferToServer.Checked;

            WorkflowAssociation newAssociation = new WorkflowAssociation();
            newAssociation.Name = this.ctrAnnotation.AssociationName;
            newAssociation.Annotation = this.ctrAnnotation.Annotation;
            newAssociation.Job = this.selectedJob;
            return newAssociation;
        }

        /// <summary>
        /// Method for Validation All Conditions/Selection before workflow associations
        /// </summary>
        /// <param name="picture">The picture.</param>
        /// <returns>bool</returns>
        private bool ValidationForAssociation(DocumentPicture picture)
        {
            if (ControllerInstance.CheckDocumentProtection(DocumentKey))
            {
                return false;
            }

            string message = string.Empty;
            string associationName = this.ctrAnnotation.AssociationName;
            if (associationName != null)
            {
                associationName = associationName.Trim();
            }
            if (picture == null && this.AssociationMode != TaskPaneKey.InsertTridentLink)
            {
                message = ResourceFetcher.GetString("ImageNotSelected");
            }
            else if (this.AssociationMode != TaskPaneKey.InsertTridentLink && picture.AppendixPicture)
            {
                message = ResourceFetcher.GetString("AssociationWithRerunResult");
            }
            else if (associationName.IsNullOrEmpty())
            {
                message = ResourceFetcher.GetString("AssociationNameBlank");
            }
            else if (this.AssociationExistsByName(this.ctrAnnotation.AssociationName))
            {
                message = string.Format(
                    ResourceFetcher.GetString("AssociationByNameAlreadyExists"),
                    this.ctrAnnotation.AssociationName);
            }
            else if (this.ControllerInstance.IsTransactionActive(DocumentKey))
            {
                message = ResourceFetcher.GetString("JobExecutionInProgress");
            }

            if (message.Length != 0)
            {
                MessageBoxHelper.ShowDefault(message, MessageBoxIcon.Warning);
                return false;
            }

            this.WorkflowExists();
            return true;
        }

        /// <summary>
        /// Workflows the name of the exists by.
        /// </summary>
        /// <param name="associationName">Name of the association.</param>
        /// <returns>bool</returns>
        private bool AssociationExistsByName(string associationName)
        {
            ReadOnlyCollection<WorkflowAssociation> associationList = ControllerInstance.GetAllWorkflowAssociations(DocumentKey);
            var result = from association in associationList
                         where associationName == association.Name
                         select association;

            return result.ToList().Count == 0 ? false : true;
        }

        /// <summary>
        /// Workflows the exists.
        /// </summary>
        private void WorkflowExists()
        {
            Model alreadyStoredModel = ControllerInstance.GetModelByDocumentKey(DocumentKey);
            if (alreadyStoredModel.Workflows.ContainsKey(this.selectedWorkflow.Id))
            {
                Workflow storedWorkflow = alreadyStoredModel.Workflows[this.selectedWorkflow.Id];
                if (storedWorkflow.Linked && !this.selectedWorkflow.Linked)
                {
                    DialogResult result = MessageBoxHelper.ShowYesNo(
                        string.Format(ResourceFetcher.GetString("LinkedWorkflowExists"), this.selectedWorkflow.Id));
                    if (DialogResult.No == result)
                    {
                        this.rbnReferToServer.Checked = true;
                        this.selectedWorkflow.Linked = true;
                    }
                }
                else if (!storedWorkflow.Linked && this.selectedWorkflow.Linked)
                {
                    DialogResult result = MessageBoxHelper.ShowYesNo(
                       string.Format(ResourceFetcher.GetString("StoredWorkflowExists"), this.selectedWorkflow.Id));

                    if (DialogResult.No == result)
                    {
                        this.rbnStoreInDocument.Checked = true;
                        this.selectedWorkflow.Linked = false;
                    }
                }
            }
        }

        /// <summary>
        /// Gets the picture information.
        /// </summary>
        /// <param name="association">The association.</param>
        private void GetPictureInformation(WorkflowAssociation association)
        {
            if (this.AssociationMode == TaskPaneKey.InsertTridentLink)
            {
                this.range = ControllerInstance.Range;
            }
            else
            {
                association.Picture = ControllerInstance.SelectedPicture;
            }
        }
      
        #endregion

        #region Event Handlers

        /// <summary>
        /// Selects the workflows user control_ on selected job changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        private void ctrSelectWorkflow_OnSelectedJobChanged(object sender, SelectedJobChangedEventArgs e)
        {
            this.selectedJob = e.Job;
            this.selectedWorkflow = e.Workflow == null ? null : e.Workflow.Clone() as Workflow;
            this.ctrWorkflowActivities.Refresh(e.Workflow);
        }

        /// <summary>
        /// Handles the OnJobListUpdate event of the ctrSelectWorkflow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.JobsUpdatedEventArgs"/> instance containing the event data.</param>
        private void ctrSelectWorkflow_OnJobListUpdate(object sender, JobsUpdatedEventArgs e)
        {
            this.btnSave.Enabled = e.Count != 0;
            this.ctrWorkflowActivities.Refresh(null);
        }

        /// <summary>
        /// Handles the OnJobDoubleClicked event of the ctrSelectWorkflow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ctrSelectWorkflow_OnJobDoubleClicked(object sender, EventArgs e)
        {
            if (selectedJob != null)
            {
                this.tabctrWorkflowAndParamaters.SelectedIndex = 1;
            }
        }

        /// <summary>
        /// Handles the OnAssociationChanged event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.AssociationChangedEventArgs"/> instance containing the event data.</param>
        private void Controller_OnAssociationChanged(object sender, AssociationChangedEventArgs e)
        {
            if (DocumentKey == e.DocumentKey)
            {
                this.ctrSelectWorkflow.RefreshJobs(true);
            }
        }

        /// <summary>
        /// Handles the Button Save Click event
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSave_Click(object sender, EventArgs e)
        {
            if (this.selectedJob == null)
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoAssociationSelected"));
                return;
            }

            this.workflowAssociation = this.CreateWorkflowAssociation();
            this.GetPictureInformation(this.workflowAssociation);
            if (!this.ValidationForAssociation(this.workflowAssociation.Picture))
            {
                return;
            }

            this.BeforeAssociationBegin();
            this.asyncAssociation = new Thread(new ThreadStart(this.StartWorkflowAssociation));
            this.asyncAssociation.Name = "Associate Workflow";
            this.asyncAssociation.SetApartmentState(ApartmentState.STA);
            this.asyncAssociation.Start();
            this.timerProgress.Start();
        }

        /// <summary>
        /// Handles the Click event of the btnCancel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (this.asyncAssociation != null)
            {
                this.isAssociationCanceled = true;
                this.StatusMessage("Cancelling workflow association...");
                this.asyncAssociation.Abort();
            }
        }

        /// <summary>
        /// Handles the AssociationProgress event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.ProgressEventArgs"/> instance containing the event data.</param>
        private void Controller_AssociationProgress(object sender, ProgressEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                this.BeginInvoke(new UICallerDelegate(this.StatusMessage), e.StatusMessage);
            }
        }

        /// <summary>
        /// Handles the Tick event of the timerProgress control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void timerProgress_Tick(object sender, EventArgs e)
        {
            if (!this.asyncAssociation.IsAlive)
            {
                this.timerProgress.Stop();
                this.AfterAssociationEnd();
            }
        }

        /// <summary>
        /// Handles the OnDocumentClosing event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.BaseEventArgs"/> instance containing the event data.</param>
        public void Controller_OnDocumentClosing(object sender, BaseEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                this.asyncAssociation.CancelThread();
            }
        }

        #endregion

        #region ITaskPaneControl Members

        /// <summary>
        /// Sets the dock position.
        /// </summary>
        /// <param name="dockPosition">The dock position.</param>
        public void SetDockPosition(TaskPaneDockPositions dockPosition)
        {
            if ((dockPosition & (TaskPaneDockPositions.Left | TaskPaneDockPositions.Right | TaskPaneDockPositions.Floating)) != 0)
            {
                this.SetVertical(true);
            }
            else
            {
                this.SetVertical(false);
            }
        }

        /// <summary>
        /// Sets the vertical.
        /// </summary>
        /// <param name="controlIsVertical">if set to <c>true</c> [control is vertical].</param>
        private void SetVertical(bool controlIsVertical)
        {
            this.SuspendLayout();

            int initialSplitterDistance = controlIsVertical ==
                true ? this.splitContainerTop.MinimumSize.Height : this.splitContainerTop.MinimumSize.Width;

            this.splitContainerBottom.SplitterDistance = initialSplitterDistance;
            this.splitContainerTop.SplitterDistance = initialSplitterDistance;

            Orientation orientation = controlIsVertical ? Orientation.Horizontal : Orientation.Vertical;

            this.splitContainerTop.Orientation = orientation;
            this.splitContainerBottom.Orientation = orientation;

            if (controlIsVertical)
            {
                this.pnlStoreWorkflow.Dock = DockStyle.Top;
                this.splitContainerComplete.SplitterDistance = (int)(this.Height * 0.83);
                this.splitContainerTop.SplitterDistance = (int)(this.Height * 0.50);
                this.splitContainerBottom.SplitterDistance = (int)(this.Height * 0.10);
            }
            else
            {
                this.splitContainerComplete.FixedPanel = FixedPanel.None;
                this.pnlStoreWorkflow.Dock = DockStyle.Left;
                int splitterDistance = this.Width / 2;
                this.splitContainerComplete.SplitterDistance = (int)(this.Height * 0.80);
                this.splitContainerTop.SplitterDistance = splitterDistance;
                this.splitContainerBottom.SplitterDistance = splitterDistance;
            }

            this.ResumeLayout();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"/>.
        /// </summary>
        void IDisposable.Dispose()
        {
            this.Disposing(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposings the specified disposing.
        /// </summary>
        /// <param name="disposing">if set to <c>true</c> [disposing].</param>
        protected virtual void Disposing(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    this.ControllerInstance.OnAssociationChanged -= new EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
                    this.ctrSelectWorkflow.OnSelectedJobChanged -= new System.EventHandler<SelectedJobChangedEventArgs>(ctrSelectWorkflow_OnSelectedJobChanged);
                    this.ctrSelectWorkflow.OnJobsUpdated -= new EventHandler<JobsUpdatedEventArgs>(ctrSelectWorkflow_OnJobListUpdate);
                    this.ctrSelectWorkflow.OnJobDoubleClicked -= new EventHandler<EventArgs>(ctrSelectWorkflow_OnJobDoubleClicked);
                    this.ControllerInstance.OnDocumentClosing -= new EventHandler<BaseEventArgs>(Controller_OnDocumentClosing);
                    this.ctrSelectWorkflow.UnregisterEventHandlers();
                    base.Disposing();
                }
            }

            disposed = true;
            base.Dispose(disposing);
        }

        #endregion
    }
}
